﻿using System.AutoUpdate.Core;
using System.AutoUpdate.Exceptions;
using System.AutoUpdate.Interfaces;
using System.Collections.Generic;
using System.IO;

namespace System.AutoUpdate.Library
{
    /// <summary>
    /// Update API for transfering versions - Background operation
    /// </summary>
    public class UpdateTransfer
    {
        private IUpdateSource _currentSource;

        public event TransferEventHandler TransferStarted;
        public event TransferEventHandler TransferCompleted;
        public event TransferEventHandler TransferProgress;

        public UpdateVersionInfo Version
        {
            get;
            private set;
        }

        /// <summary>
        /// Returns how many bytes were transferred
        /// </summary>
        public long TransferredBytes
        {
            get;
            private set;
        }

        public List<IUpdateSource> AvailableSources
        {
            get;
            private set;
        }

        public bool IsRunning
        {
            get;
            private set;
        }

        public UpdateTransfer (UpdateVersionInfo version)
        {
            Version = version;

            AvailableSources = new List<IUpdateSource> ();
        }

        /// <summary>
        /// Function transfers version to desired location
        /// </summary>
        public void TransferVersion ()
        {
            //
            // Take first source and transfer file to a local path
            //

            if (AvailableSources.Count > 0)
            {
                //
                // Find a filename that does not yet exist, by adding copy number
                //

                TransferredBytes = 0;

                int copy = 0;

                do
                {
                    Version.File.LocalFilename = UpdateVersionInfo.FilenameBuilder.Build (Version, copy);

                    copy++;
                }
                while (File.Exists (Version.File.AbsoluteLocalPath));

                //
                // Assign source and find transfer
                //

                _currentSource = AvailableSources[0];

                _currentSource.TransferProgressChanged += CurrentSourceOnTransferProgressChanged;
                _currentSource.TransferEnded += CurrentSourceOnTransferEnded;

                TransferStarted(this, new TransferEventArgs
                                      {
                                          Version = Version,
                                          Source = _currentSource
                                      });

                IsRunning = true;
                _currentSource.Transfer (Version);
            }
        }

        /// <summary>
        /// Handle source transfer ended
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        private void CurrentSourceOnTransferEnded (object sender, TransferEventArgs eventArgs)
        {
            IsRunning = false;

            //
            // Remove events
            //

            _currentSource.TransferProgressChanged -= CurrentSourceOnTransferProgressChanged;
            _currentSource.TransferEnded -= CurrentSourceOnTransferEnded;

            AvailableSources.Remove(_currentSource);

            //
            // If no exception on transfer ended, file was transferred
            //

            if ((eventArgs.Exception == null) && (File.Exists (Version.File.AbsoluteLocalPath)))
            {
                Version.File.IsTransfered = true;

                TransferredBytes = Version.File.FileSize;

                TransferCompleted (this, new TransferEventArgs
                                         {
                                             Version = Version,
                                             Source = _currentSource,
                                             TransferredBytes = eventArgs.TransferredBytes
                                         });

                _currentSource = null;
            }
            else
            {
                //
                // Delete failed file transfer if it exists
                //

                if (File.Exists (Version.File.AbsoluteLocalPath))
                {
                    File.Delete(Version.File.AbsoluteLocalPath);
                }

                if ((AvailableSources.Count > 1))
                {
                    TransferCompleted (this, new TransferEventArgs
                                             {
                                                 Version = Version,
                                                 Exception = new TransferException ("File transfer error.", eventArgs.Exception),
                                                 Source = _currentSource
                                             });

                    _currentSource = null;

                    TransferVersion ();
                }
                else
                {
                    TransferCompleted (this, new TransferEventArgs
                                             {
                                                 Version = Version,
                                                 Exception = new TransferException ("No source left to download.", eventArgs.Exception),
                                                 Source = _currentSource
                                             });

                    _currentSource = null;
                }
            }
        }

        private void CurrentSourceOnTransferProgressChanged(object sender, TransferEventArgs eventArgs)
        {
            TransferredBytes += eventArgs.TransferredBytes;

            if (TransferredBytes > Version.File.FileSize)
            {
                TransferredBytes = Version.File.FileSize;
            }

            TransferProgress(this, new TransferEventArgs
                                   {
                                       Source = _currentSource,
                                       TransferredBytes = eventArgs.TransferredBytes,
                                       Version = Version
                                   });
        }
    }
}
